﻿using System.Collections.Generic;
using System.Data;
using System.Text;
using RpcHelper;
using SqlExecHelper.Cache;
using SqlExecHelper.Column;
using SqlExecHelper.SqlValue;

namespace SqlExecHelper.Insert
{
        public class BatchInsert : ISqlBasic, IBatchInsert
        {
                private readonly IDAL _MyDAL = null;
                private BasicSqlColumn[] _Output = null;
                public BatchInsert (string table, IDAL myDAL)
                {
                        this._MyDAL = myDAL;
                        this.TableName = table;
                        this.Config = new Config.SqlBasicConfig (table);
                }
                public ISqlRunConfig Config
                {
                        get;
                }
                public TableColumn[] Column { get; set; }

                public int RowCount => this._Rows.Count;
                public string TableName { get; }

                private readonly List<object[]> _Rows = new List<object[]> ();

                public void AddRow (params object[] datas)
                {
                        if (datas.Length == this.Column.Length)
                        {
                                this._Rows.Add (datas);
                        }
                }
                private void _LoadValues (StringBuilder sql, out IDataParameter[] param)
                {
                        List<IDataParameter> list = new List<IDataParameter> (this.Column.Length * this._Rows.Count);
                        this._Rows.ForEach (a =>
                         {
                                 sql.Append ("(");
                                 this.Column.ForEach ((b, i) =>
                                 {
                                         if (!b.IsIdentify)
                                         {
                                                 IInsertSqlValue t = new InsertSqlValue (b.Name, b.SqlDbType, b.Size) { Value = a[i], IsNull = b.IsNull };
                                                 sql.Append (t.GetValue (this.Config, list));
                                                 sql.Append (",");
                                         }
                                 });
                                 sql.Remove (sql.Length - 1, 1);
                                 sql.Append ("),");
                         });
                        sql.Remove (sql.Length - 1, 1);
                        param = list.ToArray ();
                }
                public void Save ()
                {
                        if (this._Rows.Count == 0)
                        {
                                throw new ErrorException ("sql.row.null");
                        }
                        else if (this._Rows.Count == 1)
                        {
                                this._Insert (this._Rows[0]);
                                return;
                        }
                        SqlHelper.ExecuteNonQuery (this, this._MyDAL);
                }
                #region 插入单条
                private void _Insert (object[] datas)
                {
                        IInsertSqlValue[] values = this.Column.Convert (a => a.IsIdentify == false, (a, i) => new InsertSqlValue (a.Name, a.SqlDbType, a.Size) { Value = datas[i] });
                        ISqlBasic sql = new SqlInsert (values, this.Config);
                        SqlHelper.ExecuteNonQuery (sql, this._MyDAL);
                }
                private void _Insert (object[] row, out int[] ids)
                {
                        IInsertSqlValue[] values = this.Column.Convert (a => a.IsIdentify == false, (a, i) => new InsertSqlValue (a.Name, a.SqlDbType, a.Size) { Value = row[i] });
                        ISqlBasic sql = new SqlInsertById (values, this.Config, SqlDbType.Int);
                        SqlHelper.Insert (sql, this._MyDAL, out int id);
                        ids = new int[] { id };
                }
                private void _Insert (object[] row, out long[] datas)
                {
                        IInsertSqlValue[] values = this.Column.Convert (a => a.IsIdentify == false, (a, i) => new InsertSqlValue (a.Name, a.SqlDbType, a.Size) { Value = row[i] });
                        ISqlBasic sql = new SqlInsertById (values, this.Config, SqlDbType.BigInt);
                        SqlHelper.Insert (sql, this._MyDAL, out long id);
                        datas = new long[] { id };
                }
                private T[] _Insert<T> (object[] row)
                {
                        ClassStructure obj = ClassStructureCache.GetStructure (typeof (T));
                        IInsertSqlValue[] values = this.Column.Convert (a => a.IsIdentify == false, (a, i) => new InsertSqlValue (a.Name, a.SqlDbType, a.Size) { Value = row[i] });
                        ISqlBasic sql = new SqlInsertOut (values, this.Config, obj);
                        T data = SqlHelper.GetRow<T> (sql, this._MyDAL);
                        return new T[] { data };
                }
                private T[] _Insert<T> (object[] row, string column)
                {
                        IInsertSqlValue[] values = this.Column.Convert (a => a.IsIdentify == false, (a, i) => new InsertSqlValue (a.Name, a.SqlDbType, a.Size) { Value = row[i] });
                        ISqlBasic sql = new SqlInsertOut (values, this.Config, column);
                        T data = SqlHelper.GetRow<T> (sql, this._MyDAL);
                        return new T[] { data };
                }
                #endregion

                protected virtual void _InitFoot (StringBuilder sql)
                {

                }
                protected virtual string _GetOutput ()
                {
                        if (this._Output == null || this._Output.Length == 0)
                        {
                                return string.Empty;
                        }
                        StringBuilder str = new StringBuilder (" output ", 64);
                        this._Output.ForEach (a =>
                         {
                                 if (a.AliasName != null)
                                 {
                                         str.AppendFormat ("inserted.{0} as {1},", a.Name, a.AliasName);
                                 }
                                 else
                                 {
                                         str.AppendFormat ("inserted.{0},", a.Name);
                                 }
                         });
                        str.Remove (str.Length - 1, 1);
                        return str.ToString ();
                }
                public StringBuilder GenerateSql (out IDataParameter[] param)
                {
                        StringBuilder sql = new StringBuilder (512);
                        sql.AppendFormat ("insert into {0}({1}) {2} values", this.TableName, this.Column.Join (",", a => !a.IsIdentify, a => a.Name), this._GetOutput ());
                        this._LoadValues (sql, out param);
                        this._InitFoot (sql);
                        return sql;
                }

                public T[] Save<T> ()
                {
                        if (this._Rows.Count == 0)
                        {
                                throw new ErrorException ("sql.insert.null.row");
                        }
                        else if (this._Rows.Count == 1)
                        {
                                return this._Insert<T> (this._Rows[0]);
                        }
                        else
                        {
                                ClassStructure obj = ClassStructureCache.GetStructure (typeof (T));
                                this._Output = obj.GetBasicColumn ();
                                return SqlHelper.GetTable<T> (this, this._MyDAL);
                        }
                }
                public T[] Save<T> (string column)
                {
                        if (this._Rows.Count == 0)
                        {
                                throw new ErrorException ("sql.insert.null.row");
                        }
                        else if (this._Rows.Count == 1)
                        {
                                return this._Insert<T> (this._Rows[0], column);
                        }
                        else
                        {
                                this._Output = new BasicSqlColumn[]
                                {
                                        new BasicSqlColumn(column)
                                };
                                return SqlHelper.GetTable<T> (this, this._MyDAL);
                        }
                }
                public void Save (out int[] ids)
                {
                        if (this._Rows.Count == 0)
                        {
                                throw new ErrorException ("sql.insert.null.row");
                        }
                        else if (this._Rows.Count == 1)
                        {
                                this._Insert (this._Rows[0], out ids);
                        }
                        else
                        {
                                SqlHelper.BatchInsert (this, this._MyDAL, out ids);
                        }
                }
                public void Save (out long[] ids)
                {
                        if (this._Rows.Count == 0)
                        {
                                throw new ErrorException ("sql.insert.null.row");
                        }
                        else if (this._Rows.Count == 1)
                        {
                                this._Insert (this._Rows[0], out ids);
                        }
                        else
                        {
                                SqlHelper.BatchInsert (this, this._MyDAL, out ids);
                        }
                }
        }
}
